package com.detech.sbom.base.aspect;

import com.detech.sbom.base.annotation.Lock;
import com.detech.sbom.base.exception.LockException;
import com.detech.sbom.base.redis.DefaultLockKeyBuilder;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;

/**
 * 分布式锁注解实现
 *
 */
@Aspect
@Component
@Order(1)
public class LockAspect {

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @Resource
    private RedissonClient redissonClient;

    @Around("@annotation(distributedLock)")
    public Object lock(ProceedingJoinPoint joinPoint, Lock distributedLock) throws Throwable{
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String prefix = distributedLock.keyPrefix() + ":";
        prefix += StringUtils.hasText(distributedLock.name()) ? distributedLock.name() :
            signature.getMethod().getDeclaringClass().getName() + signature.getMethod().getName();
        String key = prefix + "#" + DefaultLockKeyBuilder.buildKey(joinPoint, distributedLock.keys());
        RLock lock = redissonClient.getLock(key);
        boolean hasLock = false;
        try {
            hasLock = lock.tryLock(distributedLock.waitTime(), distributedLock.expire(), distributedLock.timeUnit());
            if (hasLock) {
                return joinPoint.proceed();
            }else {
                if(distributedLock.throwError()){
                    throw new LockException(distributedLock.failMsg());
                }else{
                    if(distributedLock.print()){
                        log.info("获取分布式锁失败,key:{}",key);
                    }
                    return null;
                }
            }
        } finally {
            if (hasLock) {
                lock.unlock();
            }
        }
    }
}
